<template>
  <div class="vue3-scroll-animation" ref="containerRef">
    <div
      class="scroll-track"
      ref="trackRef"
      :class="{
        'hover-pause': hover,
        'is-animated': isAnimating,
        'direction-horizontal': horizontal,
      }"
    >
      <div class="scroll-content" ref="contentRef">
        <slot></slot>
      </div>
      <div v-if="isAnimating" class="scroll-content-clone">
        <slot></slot>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch, computed } from "vue";

// 组件属性定义
interface ScrollProps {
  /** 是否在鼠标悬停时暂停滚动动画 */
  hover?: boolean;
  /** 滚动速度步进值，值越大滚动越快 */
  step?: number;
  /** 是否使用水平滚动模式，默认垂直滚动 */
  horizontal?: boolean;
  /** 动画持续时间计算系数，调整滚动速度比例 */
  durationFactor?: number;
}

const props = withDefaults(defineProps<ScrollProps>(), {
  hover: true,
  step: 5,
  horizontal: false,
  durationFactor: 10,
});

// DOM引用
const containerRef = ref<HTMLDivElement | null>(null);
const trackRef = ref<HTMLDivElement | null>(null);
const contentRef = ref<HTMLDivElement | null>(null);

// 状态管理
const isAnimating = ref(false);

// 计算动画持续时间
const animationDuration = computed(() => {
  if (!contentRef.value) return 0;

  const contentSize = props.horizontal
    ? contentRef.value.clientWidth
    : contentRef.value.clientHeight;

  // 基于内容大小和步长计算动画持续时间
  return contentSize / (props.step * props.durationFactor);
});

// 初始化滚动状态
const initScroll = () => {
  if (!contentRef.value || !containerRef.value) return;

  const contentSize = props.horizontal
    ? contentRef.value.clientWidth
    : contentRef.value.clientHeight;

  const containerSize = props.horizontal
    ? containerRef.value.clientWidth
    : containerRef.value.clientHeight;

  // 只有当内容大小大于容器大小时才启用动画
  isAnimating.value = contentSize > containerSize;

  // 应用动画持续时间
  if (isAnimating.value && trackRef.value) {
    trackRef.value.style.animationDuration = `${animationDuration.value}s`;
  }
};

// 监听属性变化
watch([() => props.step, () => props.horizontal], () => {
  initScroll();
});

// 监听动画持续时间变化
watch(animationDuration, (newDuration) => {
  if (isAnimating.value && trackRef.value) {
    trackRef.value.style.animationDuration = `${newDuration}s`;
  }
});

// 调整窗口大小时重新初始化
const handleResize = () => {
  // 使用防抖避免频繁计算
  requestAnimationFrame(initScroll);
};

// ResizeObserver实例
let resizeObserver: ResizeObserver | null = null;

// 组件挂载时初始化
onMounted(() => {
  // 初始计算
  initScroll();

  // 设置ResizeObserver监听内容尺寸变化
  resizeObserver = new ResizeObserver((entries) => {
    // 只有当内容尺寸实际变化时才重新计算
    if (entries.length > 0) {
      handleResize();
    }
  });

  // 开始监听内容元素
  if (contentRef.value) {
    resizeObserver.observe(contentRef.value);
  }

  // 监听窗口大小变化（可选，取决于实际需求）
  window.addEventListener("resize", handleResize);
});

// 组件卸载时清理
onUnmounted(() => {
  // 断开ResizeObserver连接
  if (resizeObserver) {
    resizeObserver.disconnect();
    resizeObserver = null;
  }

  // 移除窗口大小变化监听
  window.removeEventListener("resize", handleResize);
});
</script>
<style lang="scss" scoped>
.vue3-scroll-animation {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;

  .scroll-track {
    &.direction-horizontal {
      display: flex;
      width: max-content;
      flex-wrap: nowrap;
    }
  }

  // 内容克隆元素样式保持一致
  .scroll-content,
  .scroll-content-clone {
    width: 100%;
  }

  // 水平滚动模式下内容样式
  .direction-horizontal {
    .scroll-content,
    .scroll-content-clone {
      width: auto;
      white-space: nowrap;
    }
  }
}

// 动画样式
.is-animated {
  animation-name: scrollVertical;
  animation-duration: 5s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-fill-mode: both;

  // 水平滚动动画
  &.direction-horizontal {
    animation-name: scrollHorizontal;
  }

  // 悬停暂停效果
  &.hover-pause:hover {
    animation-play-state: paused;
  }
}

// 垂直滚动动画
@keyframes scrollVertical {
  0% {
    transform: translateY(0);
  }
  100% {
    transform: translateY(-50%);
  }
}

// 水平滚动动画
@keyframes scrollHorizontal {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-50%);
  }
}
</style>
